package com.nulldev.util.uri.yt.comments;

import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.regex.Pattern;

import com.nulldev.util.java.DataSet;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.lang.json.Json;
import com.nulldev.util.lang.json.JsonArray;
import com.nulldev.util.lang.json.JsonObject;
import com.nulldev.util.lang.json.JsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.string.StringsUtil;
import com.nulldev.util.uri.URIUtil;
import com.nulldev.util.uri.URIUtil2;
import com.nulldev.util.uri.URIUtil2.IO.Entry;
import com.nulldev.util.uri.yt.VideoMeta;

public class YTComments {
	
	private static final ArrayDeque<DataSet<String, CommentMeta>> fifoQueue = 
			new ArrayDeque<DataSet<String, CommentMeta>>(512);
	
	public static void push(final String videoID, final CommentMeta data) {
		if (contains(videoID)) {
			return;
		}
		fifoQueue.push(new DataSet<String, CommentMeta>(videoID, data));
	}
	
	public static boolean contains(final String videoID) {
		for (final DataSet<String, CommentMeta> set : fifoQueue) {
			if (set.getA().equals(videoID)) {
				return true;
			}
		}
		return false;
	}
	
	public static DataSet<String, CommentMeta> get(final String videoID) {
		for (final DataSet<String, CommentMeta> set : fifoQueue) {
			if (set.getA().equals(videoID)) {
				return set;
			}
		}
		return null;
	}
	
	private static final void loadMetadata(final String videoID) {
		try {
			final String pageData = URIUtil.downloadPage(URIUtil.safeCreate("https://www.youtube.com/watch?v=" + videoID));
			push(videoID, new CommentMeta(pageData));
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Deprecated
	public static final String playbackStatRequest() {
		/*
		 * Based upon this request.
		 * https://www.youtube.com/api/stats/playback?ns=yt&el=detailpage&cpn=AFGyzdU-jPW_gZpl&docid=DaJ5tQWPS9A&ver=2&cmt=0.022&plid=AAV_HMbAflJOqwRA&ei=xW03XIeLLoeJgwPB4qKICQ&fmt=243&fs=0&rt=1.333&of=gSaNReIvM8tbK_BaHza55w&euri&lact=1393&cl=228446241&mos=0&vm=CAIQARgE&volume=100&c=WEB&cver=2.20190109&cplayer=UNIPLAYER&cbr=Chrome&cbrver=71.0.3578.98&cos=X11&hl=en_GB&cr=PL&len=173.061&fexp=23710476%2C23721898%2C23722411%2C23726562%2C23727262%2C23739610%2C23744176%2C23751767%2C23752869%2C23755886%2C23755898%2C23758087%2C23758239%2C23760558%2C23761607%2C23762649%2C23768292%2C23769730%2C23772104%2C23772497%2C23774887%2C23779061%2C23782011%2C23783454%2C23783670%2C23784105%2C23784105%2C23784117%2C23784370%2C23785338%2C23786333%2C23786732%2C23787228%2C9426788%2C9449243%2C9471235%2C9485000&rtn=11&afmt=251
		 */
		return "";
	}
	
	public static final Entry fakeCookie() {
		/*								YSC=rCQxQvhZSLA																						*/
		return new Entry("Cookie", "YSC=" + StringsUtil.randomString(11) +
				" GPS=1; VISITOR_INFO1_LIVE=" + StringsUtil.randomString(10) + "; CONSENT=WP.275880; PREF=f1=50000000&f5=30000");
	}
	
	private static final DataSet<String, Integer> request(final java.net.URL url, final Entry[] otherData, final Entry... entries) {
		return URIUtil2.IO.sendPOST(url, entries, otherData);
	}
	
	private static final Logger logger = LoggerFactory.getLogger(true, true);
	
	/*
	 * 403 Fixing
	 * Cause: Session Token. Is required to be valid...
	 * Using HACK.exe for now and hope this token DOES NOT expire.
	 */
	
	/*
	 * https://www.youtube.com/watch_fragments2_ajax [POST]
	 * Required Params:
	 * v: videoID
	 * tr: "scroll"
	 * distiller: 1
	 * ctoken: COMMENT_TOKEN
	 * frags: comments
	 * Required Form Data:
	 * SESSION_ID
	 * CLIENT_URL (youtube video url)
	 * 
	 * ===
	 * Response: JSON
	 * body {
	 * 	watch-discussion: * pre generated html code * << WITH COMMENTS DOOD
	 * }
	 * name: "other"
	 * foot: *comment_script*
	 */
	
	
	public static boolean USE_OLD_BROKEN_METHOD = true;
	
	public static final String SHOULD_BE_WORKING = "https://www.youtube.com/watch_fragments2_ajax?";
	
	public static final String URL = "https://www.youtube.com/comment_service_ajax?action_get_comments=1&pbj=1&";
			//+ "ctoken=EiYSC2o4QmZQa2NuRTJBwAEAyAEA4AEBogINKP___________wFAABgG"
			//+ "&continuation=EiYSC2o4QmZQa2NuRTJBwAEAyAEA4AEBogINKP___________wFAABgG"
			//+ "&itct=CFkQybcCIhMIwMD-sa6t3AIVTAXVCh1tbg5-KPgd";
	
		
	public static DataSet<String, LinkedList<YTComment>> getComments(final String videoID) {
		final LinkedList<YTComment> comments = new LinkedList<YTComment>();
		if (videoID.length() >= 12) {
			return new DataSet<String, LinkedList<YTComment>>("Comments from BitChute are unsupported.", comments);
		}
		/*if (!contains(videoID)) {
			logger.info("[...] Manually pushing metadata...");
			loadMetadata(videoID);
		}*/
		if (!contains(videoID)) {
			logger.error("[FAILED] Falling back...");
			try {
				final String fallbackURL = "https://www.sandracires.com/en/client/youtube/comments.php?v=" + videoID + "&page=1";
				final JsonObject obj = Json.parse(URIUtil.downloadPage(URIUtil.safeCreate(fallbackURL))).asObject();
				final JsonArray arr = obj.get("list").asArray();
				for (final JsonValue v0 : arr) {
					final JsonObject v = v0.asObject();
					final String username = v.getString("username", "N/A");
					final String comment = v.getString("text", "N/A");
					comments.add(new YTComment(username, comment, ""));
				}
				return new DataSet<String, LinkedList<YTComment>>("Fallback method used for comments.", comments);
			}catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		logger.info("[DONE] Loading comments...");
		try {
			final DataSet<String, CommentMeta> data = get(videoID);
			data.println();
			boolean OK = false;
			final String url = "https://www.youtube.com/comment_service_ajax"
					+ "?action_get_comments=1"
					+ "&pbj=1"
					+ "&ctoken=" + data.getB().getToken()
					+ "&continuation=" + data.getB().getToken()
					+ (data.getB().getITCT() != null ? "&itct=" + URIUtil.safeEncode(data.getB().getITCT()) : "");
			logger.info("using url: " + url);
			//final DataSet<String, Integer> result = request(
			//		URIUtil.safeCreate(
			//				url), new Entry[] {
			//	new Entry("session_token", URIUtil.safeEncode(data.getB().getXSRF()))	
			//}, fakeCookie());
			//logger.info("new API response: " + result.getB());
			//OK = (result.getB() >= 200 && result.getB() <= 204);
			if (!OK) {
				final String fallbackURL = "https://www.sandracires.com/en/client/youtube/comments.php?v=" + videoID + "&page=1";
				final JsonObject obj = Json.parse(URIUtil.downloadPage(URIUtil.safeCreate(fallbackURL))).asObject();
				final JsonArray arr = obj.get("list").asArray();
				for (final JsonValue v0 : arr) {
					final JsonObject v = v0.asObject();
					final String username = v.getString("username", "N/A");
					final String comment = v.getString("text", "N/A");
					comments.add(new YTComment(username, comment, ""));
				}
				return new DataSet<String, LinkedList<YTComment>>("Fallback method used for comments.", comments);
			}else {
				JVMUtil.async_println("everything seems ok...");
			}
			//System.out.println("Youtube page response: " + d.getA());
			//final Document doc = Jsoup.parse(d.getB());
			//System.out.println(doc.getElementById("comments"));
		}catch (Exception e) {
			e.printStackTrace();
		}
		return new DataSet<String, LinkedList<YTComment>>(null, comments);
	}
		
		/*
		boolean OK = false;
		if (USE_OLD_BROKEN_METHOD) {
			final Entry[] REQUIRED_HEADERS = new Entry[]{
					new Entry("Host", "www.youtube.com"),
					new Entry("Accept: *//*"),
					new Entry("Accept-Language: en-US,en;q=0.5"),
					//new Entry("Accept-Encoding: gzip, deflate, br"),
					new Entry("Referer: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("X-YouTube-Client-Name: 1"),
					new Entry("X-YouTube-Client-Version: 2.20180808"),
					new Entry("X-YouTube-Page-CL: 207856115"),
					new Entry("X-YouTube-Page-Label: youtube.ytfe.desktop_20180807_8_RC1"),
					new Entry("X-YouTube-Variants-Checksum: c1a11be9735af6dc0c282357b9cf0799"),
					new Entry("X-YouTube-Utc-Offset: 60"),
					new Entry("X-SPF-Referer: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("X-SPF-Previous: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("Content-Type: application/x-www-form-urlencoded"),			
					//new Entry("Cookie: VISITOR_INFO1_LIVE=VSN_D4VJYOU; PREF=f1=50000000&f5=30030; CONSENT=WP.26ffb0.27014f; YSC=Ew8KSrmErBA; GPS=1"),
					new Entry("Connection: keep-alive"),
					new Entry("Cache-Control: max-age=0"),
			};
			//final DataSet<String, Integer> response = URIUtil2.IO.send(URIUtil.safeCreate(URL), REQUIRED_HEADERS);
			//System.out.println("Response: " + response);
			final String url = "https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be";
			final DataSet<Integer, String> d =
					URIUtil2.PortedLegacyDownloader.downloadPage(
							URIUtil.safeCreate(url), true, REQUIRED_HEADERS);
			
			final Matcher m = COMMENTS_PATTERN.matcher(d.getB());
			final Matcher m0 = XSRF_ID_PATTERN.matcher(d.getB());
			final boolean a = m.find();
			final boolean b = m0.find();
			if (!a) { //  || !b
				return new DataSet<String, LinkedList<YTComment>>("Failed to find Comment ID!", comments);
			}
			final String COMMENT_ID = m.group(); //.replace("\",      '", "").replace(": \"", "");
			//final String XSRF_ID0 = //"QUFFLUhqbThIcEdETWkxRGFXc3NiMjROSmk1QTR2SGZiUXxBQ3Jtc0tsS2ZHcWh6SllTVFQ1S1J6S0g4czVHN3NZakJ5UDR2VXkwSzQtMkFyQnFsZ1J2cUxxVVhiajNEdFVZcDluSGg3ZTJ5MGZDMXgyVEg1MW5BbXZxTzlod1daWkxseGppWXljRDFJYlRlZHNBU3RSWjE4Z3FPYkV1ODhxbGhhNHFIN2hjNGItcG5sOW42ZXd6UDdSWXYwVXMyb3U3U1E=";
			final String XSRF_LEG = m0.group();
//			if (!XSRF_LEG.equals(token)) {
//				async_println("XSRF_ACC: " + token);
//				async_println("XSRF_GEN': " + XSRF_LEG);
//			}
			final String XSRF_ID = URLEncoder.encode(XSRF_LEG, CharsetUtil.UTF_8.name());
			final String COMPLETE_URL = URL + "ctoken=" + COMMENT_ID + "&continuation=" + COMMENT_ID; 
			final Entry[] REQUIRED_FORM_DATA = new Entry[] {
					new Entry("session_token", XSRF_ID),	
			};
			
			final Entry[] REQUIRED_HEADERS2 = new Entry[]{
					new Entry("Host", "www.youtube.com"),
					new Entry("Accept: *//*"),
					new Entry("Accept-Language: en-US,en;q=0.5"),
					//new Entry("Accept-Encoding: gzip, deflate, br"),
					new Entry("Referer: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("X-YouTube-Client-Name: 1"),
					new Entry("X-YouTube-Client-Version: 2.20180808"),
					new Entry("X-YouTube-Page-CL: 207856115"),
					new Entry("X-YouTube-Page-Label: youtube.ytfe.desktop_20180807_8_RC1"),
					new Entry("X-YouTube-Variants-Checksum: c1a11be9735af6dc0c282357b9cf0799"),
					new Entry("X-YouTube-Utc-Offset: 60"),
					new Entry("X-SPF-Referer: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("X-SPF-Previous: https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be"),
					new Entry("Content-Type: application/x-www-form-urlencoded"),			
					new Entry("Connection: keep-alive"),
					new Entry("Cache-Control: max-age=0"),
			};
			//final DataSet<String, Integer> pre_res = URIUtil2.IO.sendPOST(
			//		URIUtil.safeCreate("https://www.youtube.com/service_ajax?name=signalServiceEndpoint"), 
			//		REQUIRED_HEADERS2, 
			//		//new Proxy(Type.HTTP, new InetSocketAddress("localhost", 8081)),
			//		new Entry("sej", 
			//				"{\"clickTrackingParams\":\"CBsQm8gCGAIiEwj43JydkeDcAhXOu5wKHQbwA1co-B0=\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/service_ajax\",\"sendPost\":true}},\"signalServiceEndpoint\":{\"signal\":\"GET_UNSEEN_CONVERSATION_COUNT\"}}"),
			//		new Entry("session_token=" + XSRF_LEG),
			//		new Entry("csn", "cUlsW7jgGs738gSG4I-4BQ")
			//);
			//async_println("RESPONSE_PRE: " + pre_res.getB());
			//async_println("RESPONSE: " + pre_res.getA());
			//System.out.println(COMPLETE_URL);
			//System.out.println(COMMENT_ID);
			//System.out.println(XSRF_ID);
			final DataSet<String, Integer> response =
					URIUtil2.IO.sendPOST(URIUtil.safeCreate(COMPLETE_URL), REQUIRED_HEADERS2, 
							 REQUIRED_FORM_DATA); // new Proxy(Type.HTTP, new InetSocketAddress("localhost", 8080)),
			//System.out.println("Response: " + response.getB());
			//System.out.println("Response: " + response.getA());
			logger.error("[YouTube.Comments] Response: " + response.getB());
			if (response.getB() == 200) {
				final JsonObject comments0 = Json.parse(response.getA()).asObject();
				final JsonObject cresponse = comments0.get("response").asObject();
				final JsonObject continuationContents = cresponse.get("continuationContents").asObject();
				final JsonObject itemSectionContinuation = continuationContents.get("itemSectionContinuation").asObject();
				final JsonArray ccontents = itemSectionContinuation.get("contents").asArray();
				for (final JsonValue v0 : ccontents) {
					final JsonObject v = v0.
											asObject().get("commentThreadRenderer").asObject();
					final JsonObject c = v.get("comment").asObject().get("commentRenderer").asObject();
					final String author = c.get("authorText").asObject().getString("simpleText", "N/A");
					String comment = c.get("contentText").asObject().getString("simpleText", "N/A");
					if (c.get("contentText").asObject().hasValue("runs")) {
						comment = "";
						for (final JsonValue c0 : c.get("contentText").asObject().get("runs").asArray()) {
							final String c00 = c0.asObject().getString("text", "N/A");
							comment += c00 + 
									(c00.matches("[0-9]:[0-9][0-9]") || c00.matches("[0-9][0-9]:[0-9][0-9]") ? "" : "\n");
						}
					}
					final String thumb = c.get("authorThumbnail").asObject()
											.get("thumbnails").asArray().get(0).asObject().getString("url", "N/A");
					final long likes = c.getLong("likeCount", 0);
					comments.add(new YTComment(author, comment, thumb));
					OK = true;
				}
			}else {
				OK = false;
			}
		}else {
			//FIXME: <s>CORRECT</s>UNFUCK THIS SHIT, FUCK THIS SHIT, LITERALLY GETS A DIFFERENT TOKEN ON REQUEST, EVEN WITH THE SAME FUCKING COOKIE
			//FIXME: THIS SHIT MAY REQUIRE AN LOG_EVENT CALL TO EVEN FUCKING WORK, WHAT THE FUCK
			// EVENT IN QUESTION: https://www.youtube.com/youtubei/v1/log_event?alt=json&key=...
			final String url = "https://www.youtube.com/watch?v=" + videoID + "&feature=youtu.be";
			final DataSet<Integer, String> d =
					URIUtil2.PortedLegacyDownloader.downloadPage(
							URIUtil.safeCreate(url), true, new Entry[] {
									new Entry("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041204 Firefox/1.0 (Debian package 1.0.x.2-1)"),
									new Entry("Host", "www.youtube.com"),
									new Entry("Content-Type", "application/x-www-form-urlencoded"),
									new Entry("Pragma", "no-cache"),
									new Entry("Cache-Control: no-cache"),
							});
			
			final Matcher m = COMMENTS_PATTERN.matcher(d.getB());
			final Matcher m0 = XSRF_ID_PATTERN.matcher(d.getB());
			final boolean a = m.find();
			final boolean b = m0.find();
			if (!a) { //  || !b
				return new DataSet<String, LinkedList<YTComment>>("Failed to find Comment ID!", comments);
			}
			final String COMMENT_ID = m.group(); //.replace("\",      '", "").replace(": \"", "");
			//final String XSRF_ID0 = //"QUFFLUhqbThIcEdETWkxRGFXc3NiMjROSmk1QTR2SGZiUXxBQ3Jtc0tsS2ZHcWh6SllTVFQ1S1J6S0g4czVHN3NZakJ5UDR2VXkwSzQtMkFyQnFsZ1J2cUxxVVhiajNEdFVZcDluSGg3ZTJ5MGZDMXgyVEg1MW5BbXZxTzlod1daWkxseGppWXljRDFJYlRlZHNBU3RSWjE4Z3FPYkV1ODhxbGhhNHFIN2hjNGItcG5sOW42ZXd6UDdSWXYwVXMyb3U3U1E=";
			final String XSRF_LEG = m0.group();
//			if (!XSRF_LEG.equals(token)) {
//				async_println("XSRF_ACC: " + token);
//				async_println("XSRF_GEN': " + XSRF_LEG);
//			}
			final String XSRF_ID = URLEncoder.encode(XSRF_LEG, CharsetUtil.UTF_8.name());
			JVMUtil.async_println(XSRF_ID);
			final String COMPLETE_URL = SHOULD_BE_WORKING + "v=" + videoID +
					"&tr=time&distiller=1" + "&ctoken=" + COMMENT_ID + "&frags=comments&spf=load"; 
			JVMUtil.async_println(COMPLETE_URL);
			final Entry[] REQUIRED_FORM_DATA = new Entry[] {
					new Entry("session_token", XSRF_ID),
			};
			final DataSet<String, Integer> response =
					URIUtil2.IO.sendPOST(URIUtil.safeCreate(COMPLETE_URL), new Entry[] {
							new Entry("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041204 Firefox/1.0 (Debian package 1.0.x.2-1)"),
							new Entry("Cookie", "VISITOR_INFO1_LIVE=NQxgqQ0XNCg; PREF=f1=50000000&f5=30010; YSC=-NwJDdr7JIM; GPS=1"),
							new Entry("Host", "www.youtube.com"),
							new Entry("Content-Type", "application/x-www-form-urlencoded"),
							new Entry("Pragma", "no-cache"),
							new Entry("Cache-Control: no-cache"),
					}, 
							REQUIRED_FORM_DATA); //new Proxy(Type.HTTP, new InetSocketAddress("localhost", 8080)),
			
			logger.info("NEW METHOD RESPONSE: " + response.getB());
			OK = false;
		}*/
}
